using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.Validation;
using Xms.Abp.Data;
using Xms.Abp.Domain;
using ConnStrings = Volo.Abp.Data.ConnectionStrings;

namespace TenantManagement;

[Index(nameof(Name), IsUnique = true)]
public class Tenant : FullAuditedAggregateRoot<Guid>, IHasEnabled, IHasDisplayName<Guid>, IHasName<Guid>
{
    [NotNull]
    [Required]
    [DynamicMaxLength(typeof(TenantConsts), nameof(TenantConsts.MaxNameLength))]
    public virtual string Name { get; set; }

    [NotNull]
    [Required]
    [DynamicMaxLength(typeof(TenantConsts), nameof(TenantConsts.MaxDisplayNameLength))]
    public virtual string DisplayName { get; set; }

    public virtual bool IsEnabled { get; set; }

    [DynamicMaxLength(typeof(TenantConsts), nameof(TenantConsts.MaxFaviconLength))]
    public virtual string Favicon { get; set; }

    [DynamicMaxLength(typeof(TenantConsts), nameof(TenantConsts.MaxLogoLength))]
    public virtual string Logo { get; set; }

    [DynamicMaxLength(typeof(TenantConsts), nameof(TenantConsts.MaxLargeLogoLength))]
    public virtual string LargeLogo { get; set; }

    public virtual string Remark { get; set; }

    [ForeignKey(nameof(TenantConnString.TenantId))]
    public virtual ICollection<TenantConnString> ConnectionStrings { get; protected set; }

    [ForeignKey(nameof(TenantDomain.TenantId))]
    public virtual ICollection<TenantDomain> Domains { get; protected set; }

    protected Tenant()
    {
        IsEnabled = true;
    }

    public Tenant(Guid id, [NotNull] string no, [NotNull] string displayName)
        : base(id)
    {
        SetNo(no);
        SetDisplayName(displayName);

        ConnectionStrings = new List<TenantConnString>();
        Domains = new List<TenantDomain>();
        IsEnabled = true;
    }

    [CanBeNull]
    public virtual string FindDefaultConnectionString()
    {
        return FindConnectionString(ConnStrings.DefaultConnectionStringName);
    }

    [CanBeNull]
    public virtual string FindConnectionString(string name)
    {
        return ConnectionStrings.FirstOrDefault(c => c.Name == name)?.Value;
    }

    public virtual void SetDefaultConnectionString(Guid id, string connectionString)
    {
        SetConnectionString(id, ConnStrings.DefaultConnectionStringName, connectionString);
    }

    public virtual void SetConnectionString(Guid id, string name, string connectionString)
    {
        var tenantConnectionString = ConnectionStrings.FirstOrDefault(x => x.Name == name);

        if (tenantConnectionString != null)
        {
            tenantConnectionString.SetValue(connectionString);
        }
        else
        {
            ConnectionStrings.Add(new TenantConnString(id, Id, name, connectionString));
        }
    }

    public virtual void RemoveDefaultConnectionString()
    {
        RemoveConnectionString(ConnStrings.DefaultConnectionStringName);
    }

    public virtual void RemoveConnectionString(string name)
    {
        var tenantConnectionString = ConnectionStrings.FirstOrDefault(x => x.Name == name);

        if (tenantConnectionString != null)
        {
            ConnectionStrings.Remove(tenantConnectionString);
        }
    }

    [CanBeNull]
    public virtual string FindDefaultDomainString()
    {
        return FindDomain(TenantDomainConsts.DefaultName);
    }

    [CanBeNull]
    public virtual string FindDomain(string name)
    {
        return Domains.FirstOrDefault(c => c.Name == name)?.Value;
    }

    public virtual void SetDefaultDomain(Guid id, string domain)
    {
        SetDomain(id, TenantDomainConsts.DefaultName, domain);
    }

    public virtual void SetDomain(Guid id, string name, string domain)
    {
        var tenantDomain = Domains.FirstOrDefault(x => x.Name == name);

        if (tenantDomain != null)
        {
            tenantDomain.SetValue(domain);
        }
        else
        {
            Domains.Add(new TenantDomain(id, Id, name, domain));
        }
    }

    public virtual void RemoveDefaultDomain()
    {
        RemoveDomain(TenantDomainConsts.DefaultName);
    }

    public virtual void RemoveDomain(string name)
    {
        var tenantDomain = Domains.FirstOrDefault(x => x.Name == name);

        if (tenantDomain != null)
        {
            Domains.Remove(tenantDomain);
        }
    }

    protected internal virtual void SetNo([NotNull] string name)
    {
        Name = Check.NotNullOrWhiteSpace(name, nameof(name), TenantConsts.MaxNameLength);
    }

    protected internal virtual void SetDisplayName([NotNull] string displayName)
    {
        DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), TenantConsts.MaxDisplayNameLength);
    }
}
